package net.fxug.hokuriku.chocolat.service
{
    import com.adobe.errors.IllegalStateError;

    import flash.errors.IllegalOperationError;
    import flash.events.EventDispatcher;
    import flash.utils.Dictionary;

    import net.fxug.hokuriku.chocolat.dao.DatabaseManager;
    import net.fxug.hokuriku.chocolat.dao.VoteDao;
    import net.fxug.hokuriku.chocolat.events.VoteEvent;
    import net.fxug.hokuriku.chocolat.events.VoteStatusEvent;
    import net.fxug.hokuriku.chocolat.model.VoteData;

    //-------------------------------------------------------------------------
    //
    //  Events
    //
    //-------------------------------------------------------------------------
    /**
     * へぇイベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteEvent.GOOD_EVENT
     */
    [Event(name="goodEvent", type="net.fxug.hokuriku.chocolat.events.VoteEvent")]
    /**
     * ぶぅイベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteEvent.BAD_EVENT
     */
    [Event(name="badEvent", type="net.fxug.hokuriku.chocolat.events.VoteEvent")]
    /**
     * 失敗イベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteEvent.FAILED_EVENT
     */
    [Event(name="failedEvent", type="net.fxug.hokuriku.chocolat.events.VoteEvent")]
    /**
     * HTTPステータスイベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteEvent.STATUS_EVENT
     */
    [Event(name="statusEvent", type="net.fxug.hokuriku.chocolat.events.VoteEvent")]

    /**
     * 準備完了イベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteStatusEvent.READY_EVENT
     */
    [Event(name="readyEvent", type="net.fxug.hokuriku.chocolat.events.VoteStatusEvent")]
    /**
     * 活性化イベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteStatusEvent.ACTIVE_EVENT
     */
    [Event(name="activeEvent", type="net.fxug.hokuriku.chocolat.events.VoteStatusEvent")]
    /**
     * 不活性化イベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteStatusEvent.INACTIVE_EVENT
     */
    [Event(name="inactiveEvent", type="net.fxug.hokuriku.chocolat.events.VoteStatusEvent")]

//    /**
//     * タイマーに影響を与えるつぶやきの上限達成イベント
//     * FIXME 現行実装では、へぇ/ぶぅイベントよりも前に実行される。また、イベントのdispatchは上限を超えた場合に1回だけ実行される。
//     * @eventType net.fxug.hokuriku.chocolat.events.VoteStatusEvent.MAX_VOTE_EVENT
//     */
//    [Event(name="maxVoteEvent", type="net.fxug.hokuriku.chocolat.events.VoteStatusEvent")]

    /**
     * 投票処理の総括クラス。シングルトン。
     * このクラスはサーバモードでTimerスタート時にインスタンスを生成するようにしてください。
     *
     * 主たる機能として、IVoteWatcherを実装するクラスのイベントをこのクラスでプロキシし、View側空の参照先が単一に
     * なる事を目的としたManagerとして利用される事を想定しています。
     *
     * FIXNE あー、設計ミスったかも。IVoteWatcherもIEventDispatcherを継承してイベント伝播で処理する方がよかったかorz...
     * 		 やっぱりAbstractVoteWatcherみたいなものを作ってそっちにEventDispatcherを設定して...時間があったら要リファクタリング。
     * 		 修正しても、view層には影響が出ないはず。
     * @author hikaruworld
     */
    public class VoteManager extends EventDispatcher
    {
        //-------------------------------------------------------------------------
        //
        // Class Static Variables
        //
        //-------------------------------------------------------------------------
        /** 自身の一意なインスタンス */
        private static var instance:VoteManager;
        //-------------------------------------------------------------------------
        //
        // Variables
        //
        //-------------------------------------------------------------------------
        /**
         * 監視対象のインターフェイス群
         * このプロパティはready()メソッドを実行する前に、設定を行う必要があります。
         * ready()メソッド実行後はread-onlyになるため、再設定を行うことは出来ません。
         *
         * ここで定義されたそれぞれの実装クラスは内部でDictonaryのKeyとして利用されます。
         */
        private var _targetInterfaces:Vector.<IVoteWatcher>;
//        /**
//         * ユーザ別のつぶやき回数を保持するDictionary
//         * int : VoteInfo
//         */
////        private var votedMap:Object;
//        private var votedMap:Dictionary;

        /**
         * インターフェイスの準備完了判定用Map
         * VoteStatusEvent.READY_EVENTをdispatchする際に判定用として参照される。
         */
        private var interfacesReadyMap:Dictionary;
        /**
         * 起動フラグ。
         * 内部的にREADY_EVENTがdispatchされているかを判断する。
         */
        private var isInterfaceReady:Boolean;
        /** DB管理クラス */
        private var databaseManager:DatabaseManager;
        /** 登録対象のセッションID */
        private var sessionId:int;

        //-------------------------------------------------------------------------
        //
        // Class Static Method
        //
        //-------------------------------------------------------------------------
        /**
         * 自身の一意なインスタンスを取得する
         * @return 一意なVoteManagerインスタンス
         */
        public static function getInstance():VoteManager
        {
            if (instance == null)
            {
                instance = new VoteManager(new SingletonBlockr());
            }
            return instance;
        }

        //-------------------------------------------------------------------------
        //
        // Contracture
        //
        //-------------------------------------------------------------------------
        /**
         * コンストラクタ
         * @param blockr 外部からのインスタンス生成抑止用
         * @throws	flash.errors.IllegalOperationError 外部からインスタンス化が行われた場合にthrowされる。
         */
        public function VoteManager(blockr:SingletonBlockr = null)
        {
            if (blockr == null)
            {
                throw new IllegalOperationError("This class cannot instance");
            }
//            this.votedMap = new Dictionary();
            this.interfacesReadyMap = new Dictionary();
            this._targetInterfaces = Vector.<IVoteWatcher>([]);

            this.databaseManager = DatabaseManager.getInstance();
        }

        //-------------------------------------------------------------------------
        //
        // Method
        //
        //-------------------------------------------------------------------------
        /**
         * 投票処理の待ち状態にする。
         * このメソッドは、VoteManagerからdispatchされるVoteStatusEvent.READY_EVENTのリスナとして設定することを推奨します。
         * @param sessionId 登録対象のsessionId
         */
        public function ready(sessionId:int):void
        {
            if (this._targetInterfaces == null || this._targetInterfaces.length == 0)
            {
                throw new IllegalOperationError("_targetInterfaces has not exist...");
            }
            this.sessionId = sessionId;
            this._targetInterfaces.forEach(function(item:IVoteWatcher, index:int, vector:Vector.<IVoteWatcher>):void
                {
                    trace(item, index);
                    item.start();
                });
        }

        /**
         * 現在有効になっているインターフェイスの一覧を返す。
         * @return 有効な投票管理インターフェイスの一覧。存在しない場合はnullを返す。
         */
        public function enableInterfaces():Vector.<IVoteWatcher>
        {
            if (this._targetInterfaces == null)
            {
                return null;
            }

            var ret:Vector.<IVoteWatcher> = Vector.<IVoteWatcher>([]);
            var index:int = this._targetInterfaces.length;
            for (var i:int = 0; i < index; i++)
            {
                var tmp:IVoteWatcher = this._targetInterfaces[i];
                if (tmp.isEnabled())
                {
                    ret.push(ret);
                }
            }

            return ret;
        }

        /**
         * 投票処理を終了する。
         */
        public function close():void
        {
            this._targetInterfaces.forEach(function(item:IVoteWatcher, index:int, vector:Vector.<IVoteWatcher>):void
                {
                    trace(item, index);
                    item.destory();
                });
        }

        /**
         * 準備完了を通知します。
         * このメソッドは、IVoteManagerの実装クラス側でインターフェイスの待ち受け準備が完了した時点で実装クラスでかならず実行してください。
         *
         * 注意：このメソッドによってVoteStatusEvent.READY_EVENTをdispatchしますが、
         * すべての実装インターフィエスが準備完了になっていない場合、イベントのdispatchが行われません。
         */
        internal function onReady(key:IVoteWatcher):void
        {
            // 自身のインスタンスが準備完了した事を登録
            // 重複呼び出しが発生している場合はエラーを投げる。
            if (this.interfacesReadyMap[key])
            {
                //TODO カスタムエラー化(重複呼び出しを禁止)
                throw new IllegalStateError("登録しようとした対象インスタンス::" + key + " は既に登録されています。");
            }
            this.interfacesReadyMap[key] = true;

            // 登録されているインターフェイスがすべて準備完了状態になっているか精査
            var resultBool:Boolean = this._targetInterfaces.every(function callback(item:IVoteWatcher, index:int, vector:Vector.<IVoteWatcher>):Boolean
                {
                    return interfacesReadyMap[item];
                }, this);
            if (!resultBool)
            {
                trace(key + "::からonReadyが呼び出されましたが、すべての通知クラスの準備が整っていないためVoteStatusEvent.READY_EVENTのdispatchを行いません。");
                return;
            }
            trace("VoteManagerの保持するすべての通知クラスの実行準備が整いました");
            this.isInterfaceReady = true;
            var voteStatusEvent:VoteStatusEvent = new VoteStatusEvent(VoteStatusEvent.READY_EVENT, this._targetInterfaces);
            this.dispatchEvent(voteStatusEvent);
        }

        /**
         * 投票処理を発行する。
         * @param voteEvent 投票イベント
         */
        internal function dispatchVote(voteEvent:VoteEvent):void
        {
            // Voetの実行回数をカウントしておく
//            this.votedCntup(voteEvent);
            //TODO つぶやき情報をDBに登録
            var voteArray:Array = voteEvent.tweetArray as Array;

            var baseDate:Date = new Date();

            var voteDaoArray:Array = [];
            var index:int = voteArray.length;
            for (var i:int = 0; i < index; i++)
            {
                var twet:VoteData = voteEvent.tweetArray[i] as VoteData;
                var voteDao:VoteDao = new VoteDao();
                voteDao.datetime = baseDate;
                voteDao.protocol = voteEvent.type == VoteEvent.GOOD_EVENT;
                voteDao.tweet = twet.text;
                voteDao.userId = twet.id;
                voteDao.sessionId = this.sessionId;
                voteDao.route = twet.route;
                voteDaoArray.push(voteDao);
            }
            this.databaseManager.insertMultiVote(voteDaoArray);

            this.dispatchEvent(voteEvent);
        }

        /**
         * 投票管理機構の状態変化を発行する
         * @param voteStatusEvent
         */
        internal function dispatchStatus(voteStatusEvent:VoteStatusEvent):void
        {
            this.dispatchEvent(voteStatusEvent);
        }

        /**
         * 投票情報をカウントアップする。
         * FIXME 単体コミットを繰り返すのでパフォーマンス注意
         * TODO Twitterのデータ登録時にSocketから登録データが来たような場合に、SQLStatementを実行中に処理してしまうはず。
         * 出来ればこのManagerレベルか、DatabaesManagerレベルでキューを作って、登録完了時にキューにデータが存在している場合は
         * 再度処理を行っていくような実装にしないと問題がおきそう。
         */
        private function votedCntup(voteEvent:VoteEvent):void
        {
            if (voteEvent.type != VoteEvent.GOOD_EVENT && voteEvent.type != VoteEvent.BAD_EVENT)
            {
                throw new ArgumentError("引数 VoteEvent#type::" + voteEvent.type + " は適切な引数ではありません。");
            }
            var baseDate:Date = new Date();
            var index:int = voteEvent.tweetArray.length;
            var voteAry:Array = [];
            for (var i:int = 0; i < index; i++)
            {
                var twet:VoteData = voteEvent.tweetArray[i] as VoteData;
                if (twet == null)
                {
                    continue;
                }
                else
                {
                    // 存在する場合はDBに登録する。
                    var voteDao:VoteDao = new VoteDao();
                    voteDao.datetime = baseDate;
                    voteDao.protocol = voteEvent.type == VoteEvent.GOOD_EVENT;
                    voteDao.tweet = twet.text;
                    voteDao.userId = twet.id;
                    voteDao.sessionId = this.sessionId;
                    voteDao.route = twet.route;
                        //TODO ROUTEの取得

//                    this.databaseManager.addEventListener(SQLVoteEvent.INSERT_VOTE_EVENT);
//                    this.databaseManager.insertVote(voteDao);
//                    this.databaseManager.addEventListener(SQLVoteEvent.INSERT_VOTE_EVENT, function(e:SQLVoteEvent):void
//                        {
//
//
//                      });
//
//					this.databaseManager.addEventListener(SQLVoteEvent.INSERT_VOTE_EVENT, function(event:SQLVoteEvent):void
//					{
//						hoge(event
//					});
//                    voteAry.push(voteDao);
                }
//                var votedInfo:VoteInfo = this.votedMap[twet.id];
//                if (!votedInfo)
//                {
//                    votedInfo = new VoteInfo(twet.id as int);
//                }
//                var check:Boolean = votedInfo.addAndCheck(voteEvent.type);
////                if (check)
////                {
////                    var statusEvent:VoteStatusEvent = new VoteStatusEvent(VoteStatusEvent.MAX_VOTE_EVENT, votedInfo);
////                    this.dispatchStatus(statusEvent);
////                }
//
//                this.votedMap[twet.id] = votedInfo;
            }
//            // DBに登録(複数存在する場合は再帰的に処理)
//            if (voteAry.length == 0)
//            {
//                return;
//            }
////            if (voteAry.length == 1)
////            {
////                this.databaseManager.insertVote(voteAry[0]);
////                return;
////            }
////
//            var func:Function = function(voteEvent:SQLVoteEvent):void
//                {
//                    hoge(voteEvent, voteAry, 1, func);
//                };
//            this.databaseManager.addEventListener(SQLVoteEvent.INSERT_VOTE_EVENT, func);
//            this.databaseManager.insertVote(voteAry[0]);
//            // FIXME これは実は結構危ないかも...
//            // 明示的にSQLVote.INSERT_VOTE_EVENTをdispatchして再帰を実行
////            this.databaseManager.dispatchEvent(new SQLVoteEvent(SQLVoteEvent.INSERT_VOTE_EVENT, null));
        }

//        private function hoge(e:SQLVoteEvent, v:Array, i:int, func:Function):void
//        {
//            this.databaseManager.removeEventListener(SQLVoteEvent.INSERT_VOTE_EVENT, func);
//            if (i > v.length)
//            {
//                return;
//            }
//
//            var func2:Function = function(e:SQLVoteEvent):void
//                {
//                    hoge(e, v, i++, func2);
//                }
//            this.databaseManager.addEventListener(SQLVoteEvent.INSERT_VOTE_EVENT, func2);
//
//            var vote:VoteDao = v[i];
//            this.databaseManager.insertVote(vote);
//        }

        //---------------------------------------------------------------------
        //
        // IDEによる自動生成ロジック
        //
        //---------------------------------------------------------------------
        /**
         * @private
         * @param value Vector.<IVoteWatcher>
         */
        public function set targetInterfaces(value:Vector.<IVoteWatcher>):void
        {
            if (this.isInterfaceReady)
            {
                throw new IllegalStateError("既にサーバ待ち受け状態が活性化されているため、対象インターフェイス一覧を変更する事は出来ません。");
            }
            this._targetInterfaces = value;
        }
    }
}

/**
 * インスタンス生成抑止用
 */
internal class SingletonBlockr
{
}
